use crate::rating::*;

#[derive(Debug, PartialEq)]
pub enum RuleResult {
    Accept,
    Reject,
    Next,
    Jump(String),
}

#[derive(Debug, PartialEq)]
pub enum RuleOpertion {
    Great,
    Less,
}

#[derive(Debug, PartialEq)]
pub enum Rule {
    Accept,
    Reject,
    Jump(JumpRule),
    ConditionJump(ConditionJumpRule),
    ConditionAccept(ConditionAcceptRule),
}

impl Rule {
    pub fn query(&self, rating: &Rating) -> RuleResult {
        match self {
            Rule::Accept => RuleResult::Accept,
            Rule::Reject => RuleResult::Reject,
            Rule::Jump(rule) => {
                RuleResult::Jump(rule.workflow.clone())
            }
            Rule::ConditionJump(rule) => {
                let part = rule.part;
                match rule.oper {
                    RuleOpertion::Great => {
                        if rating.part(part) > rule.value {
                            RuleResult::Jump(rule.workflow.clone())
                        } else {
                            RuleResult::Next
                        }
                    }
                    RuleOpertion::Less => {
                        if rating.part(part) < rule.value {
                            RuleResult::Jump(rule.workflow.clone())
                        } else {
                            RuleResult::Next
                        }
                    }
                }
            }
            Rule::ConditionAccept(rule) => {
                let part = rule.part;
                match rule.oper {
                    RuleOpertion::Great => {
                        if rating.part(part) > rule.value {
                            if rule.accept { RuleResult::Accept } else { RuleResult::Reject }
                        } else {
                            RuleResult::Next
                        }
                    }
                    RuleOpertion::Less => {
                        if rating.part(part) < rule.value {
                            if rule.accept { RuleResult::Accept } else { RuleResult::Reject }
                        } else {
                            RuleResult::Next
                        }
                    }
                }
            }
        }
    }
}

impl From<&str> for Rule {
    fn from(s: &str) -> Self {
        match s {
            "A" => Rule::Accept,
            "R" => Rule::Reject,
            _ => {
                // a<2006:qkq, m>2090:A, rfg
                match s.find(':') {
                    Some(colon_pos) => {
                        // Get part, oper and value
                        let part: PartKind = match &s[..1] {
                            "x" => PartKind::Extremely,
                            "m" => PartKind::Musical,
                            "a" => PartKind::Aerodynamic,
                            "s" => PartKind::Shiny,
                            _ => panic!("Error part type")
                        };
                        let oper = match &s[1..2] {
                            ">" => RuleOpertion::Great,
                            "<" => RuleOpertion::Less,
                            _ => panic!("Error oper")
                        };
                        let value: i32 = s[2..colon_pos].parse().expect("Error condition value");
                        let workflow = &s[colon_pos+1..];
                        match workflow {
                            "A" => Rule::ConditionAccept(ConditionAcceptRule::new(part, oper, value, true)),
                            "R" => Rule::ConditionAccept(ConditionAcceptRule::new(part, oper, value, false)),
                            _ => Rule::ConditionJump(ConditionJumpRule::new(part, oper, value, workflow.to_string())),    
                        }
                    }
                    None => {
                        Rule::Jump(JumpRule::new(s.to_string()))
                    }
                }

            }
        }
    }
}

impl Rule {

}

#[derive(Debug, PartialEq)]
pub struct AcceptRule {}

impl AcceptRule {
    pub fn new() -> Self {
        Self {}
    }
}

#[derive(Debug, PartialEq)]
pub struct RejectRule {}

impl RejectRule {
    pub fn new() -> Self {
        Self {}
    }
}

#[derive(Debug, PartialEq)]
pub struct JumpRule {
    workflow: String,
}

impl JumpRule {
    pub fn new(workflow: String) -> Self {
        Self { workflow }
    }
}

#[derive(Debug, PartialEq)]
pub struct ConditionJumpRule {
    part: PartKind,
    oper: RuleOpertion,
    value: i32,
    workflow: String,   
}

impl ConditionJumpRule {
    pub fn new(part: PartKind, oper: RuleOpertion, value: i32, workflow: String) -> Self {
        Self { 
            part,
            oper,
            value,
            workflow
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct ConditionAcceptRule {
    part: PartKind,
    oper: RuleOpertion,
    value: i32,
    accept: bool,   
}

impl ConditionAcceptRule {
    pub fn new(part: PartKind, oper: RuleOpertion, value: i32, accept: bool) -> Self {
        Self { 
            part,
            oper,
            value,
            accept
        }
    }
}
